
ENT.Type = "anim"
ENT.Base = "base_gmodentity"

ENT.PrintName = "Server"
ENT.Author = "[GU]|ZLC|thomasfn"
ENT.Contact = "Don't."
ENT.Instructions = "Spawn it, then press e to use it."
ENT.Purpose = ""

ENT.Spawnable = false
ENT.AdminSpawnable = false

ENT.ItemModel = "models/props_lab/harddrive02.mdl"
util.PrecacheModel( ENT.ItemModel )

ENT.IsServer = true
ENT.ServerType = ""

if (SERVER) then

	AddCSLuaFile( "shared.lua" )
	
	ENT.NextUse = CurTime()

	function ENT:Initialize()
		self.Entity:SetModel(self.ItemModel)
		self.Entity:PhysicsInit(SOLID_VPHYSICS)
		self.Entity:SetMoveType(MOVETYPE_VPHYSICS)
		self.Entity:SetSolid(SOLID_VPHYSICS)
		
		if (!self.Data) then self.Data = {} end
		if (!self.Data[self:EntIndex()]) then self.Data[self:EntIndex()] = {} end

		self:UpdateBool( "IsOn", false )
		self.Data[self:EntIndex()].Router = nil
		self:UpdateStr( "IP", "192.168.0.1" )
		self.Data[self:EntIndex()].Online = false
		self.Data[self:EntIndex()].PortList = {}
		self.Data[self:EntIndex()].ServerApp = {}
		
		self:OpenPort( 50, "ent" ); -- Ping Port
		self:OpenPort( 73, "ent" ); -- Global List Port
		
		self:SetServerType( "base" )
		
		local phys = self.Entity:GetPhysicsObject()
		if (phys && phys:IsValid()) then phys:Wake() end
		
		PCBeam:BeamVarsToAll( self )
	end
	
	function ENT:SetServerType( stype )
		local app = PCMod.ServerApps[ stype ]
		if (!app) then app = PCMod.ServerApps[ "base" ] end
		app.Server = self
		self.Data[ self:EntIndex() ].ServerApp = app
		app:Initialize()
		self:UpdateStr( "AppName", app.Name )
	end
	
	function ENT:Use( activator, ply )
		if (CurTime() < self.NextUse) then return end
		self.NextUse = CurTime()+1
		local IsOn = self.Data[self:EntIndex()].IsOn
		if (!IsOn) then
			self:UpdateBool( "IsOn", true )
			ply:PrintMessage( HUD_PRINTTALK, "Server is now on." )
			
			self:TurnedOn()
			
			return
		end
		self:UpdateBool( "IsOn", false )
		ply:PrintMessage( HUD_PRINTTALK, "Server is now off." )

		self:TurnedOff()
	end
	
	function ENT:TurnedOn()
		// Register us with router
		local router = self.Data[self:EntIndex()].Router
		if ((router) && (router:IsValid())) then
			ip = router:RegisterPC( self )
			if (ip) then
				self:SetIP( ip )
				self:UpdateBool( "Online", true )
			end
		else
			self.Data[self:EntIndex()].Router = nil
		end
		
		// Call the ServerApp hook
		if (self.Data[self:EntIndex()].ServerApp) then self.Data[self:EntIndex()].ServerApp:StartUp() end
	end
	
	function ENT:TurnedOff()
		self:UpdateStr( "IP", "192.168.0.1" )
		
		// Unregister us
		local router = self.Data[self:EntIndex()].Router
		if ((router) && (router:IsValid())) then
			router:UnregisterPC( self )
			self:SetIP( "192.168.0.1" )
			self:UpdateBool( "Online", false )
		else
			self.Data[self:EntIndex()].Router = nil
		end
		
		// Call the ServerApp hook
		if (self.Data[self:EntIndex()].ServerApp) then self.Data[self:EntIndex()].ServerApp:ShutDown() end
	end
	
	function ENT:OnRemove()
		PCBeam:ResetEntity( self )
		PCBeam:BeamVarsToAll( self )
		if (self.Data[self:EntIndex()].ServerApp) then self.Data[self:EntIndex()].ServerApp:KillHost() end
	end
	
	function ENT:DoPacket( Packet )
		// This is called when a packet has been aimed at a port opened by the OS
		if (!Packet) then return end		

		// ALL SERVERS
		local port = Packet[3]
		local ip = Packet[1]
		if (Packet[4][1] == "ping") and (port == 50) then
			// We have been pinged!
		end
	
	end
	
	function ENT:OpenPort( port, command )
		if (self:PortOpen( port )) then return end
		table.insert( self.Data[self:EntIndex()].PortList, {port,command} )
	end
	
	function ENT:ClosePort( port )
		if (!self:PortOpen( port )) then return end
		ports = self.Data[self:EntIndex()].PortList
		for i=1,#ports do
			if (ports[i][1] == port) then
				table.remove( self.Data[self:EntIndex()].PortList, i )
			end
		end
	end
	
	function ENT:PortOpen( port )
		ports = self.Data[self:EntIndex()].PortList
		for i=1,#ports do
			if (ports[i][1] == port) then return true end
		end
	end
	
	function ENT:GetPortCommand( port )
		ports = self.Data[self:EntIndex()].PortList
		for i=1,#ports do
			if (ports[i][1] == port) then return ports[i][2] end
		end
		return "none"
	end
	
	// DeliveryCode:
	// 0 = Cannot Deliver
	// 1 = Delivered
	// 2 = Destination not found
	// 3 = No Network
	// 4 = Packet Denied: Closed Port
	// 5 = Packet Denied: Blocked IP
	// 6 = Packet Denied: Invalid packet

	function ENT:SendPacket( Packet )
		if (!Packet) then return 6 end
		dest = Packet[2]
		if (!dest) then return 6 end
		if (dest == "127.0.0.1") then
			return self:PacketDelivered( Packet )
		end
		if (!self.Data[self:EntIndex()].Router) or (!self.Data[self:EntIndex()].Online) then return 3 end
		return self.Data[self:EntIndex()].Router:DeliverPacket( Packet )
	end
	
	function ENT:PacketDelivered( Packet )
		if (!self.Data[self:EntIndex()].IsOn) then return 0 end
		
		// First we deal with the packet header
		if (!Packet) then return 6 end
		source = Packet[1]
		dest = Packet[2]
		port = Packet[3]
		body = Packet[4]
		myip = self.Data[self:EntIndex()].IP
		if (!self:PortOpen(port)) then return 4 end
		if (dest != myip) and (!((dest == "127.0.0.1") and (source == myip))) then return 0 end
		if (self:IPBlocked(source)) then return 5 end
		// Now we deal with the packet body
		com = self:GetPortCommand( port )
		if (com == "none") then
			return 1
		end
		if (com == "ent") then
			self:DoPacket( Packet )
			return 1
		end
		if (com == "sapp") then			
			// Call the ServerApp hook
			if (self.Data[self:EntIndex()].ServerApp) then self.Data[self:EntIndex()].ServerApp:PacketRecieved( Packet ) end
			return 1
		end
		return 6
	end
	
	function ENT:IPBlocked( IP )
		return false
	end
	
	function ENT:Think()
		if (!self.Data[self:EntIndex()].IsOn) then return end
		
		// If we do not have a router
		if (!self.Data[self:EntIndex()].Router) then
			// Make us offline
			self:UpdateBool( "Online", false )
			self:SetIP( "192.168.0.1" )
		end
		
		// If we DO have a router AND we are offline
		if (self.Data[self:EntIndex()].Router) && (!self.Data[self:EntIndex()].Online) then
			// Attempt a connection
			ip = self.Data[self:EntIndex()].Router:RegisterPC( self )
			if (ip) then
				self:SetIP( ip )
				self:UpdateBool( "Online", true )
			end
		end
	end
	
	function ENT:UpdateInt( var, value )
		self.Data[self:EntIndex()][var] = value
		PCBeam:SetValue( self, var, value )
	end
	
	function ENT:UpdateBool( var, value )
		self.Data[self:EntIndex()][var] = value
		PCBeam:SetValue( self, var, value )
	end
	
	function ENT:UpdateStr( var, value )
		self.Data[self:EntIndex()][var] = value
		PCBeam:SetValue( self, var, value )
	end
	
	function ENT:SetIP( IP )
		self.Data[self:EntIndex()].IP = IP
		self:UpdateStr( "IP", IP )
	end
	
end

if (CLIENT) then

	function ENT:Draw()
		// Draw our entity
		self.BaseClass.Draw( self )
	end
	
end